package top.codedance.iotp.client.net;

import com.alibaba.fastjson.JSONObject;
import top.codedance.iotp.client.ClientApplication;
import top.codedance.iotp.client.plugins.async.push.PushExecuterImpl;
import top.codedance.iotp.common.util.BytesBuilder;
import com.koloboke.collect.map.hash.HashObjObjMaps;
import io.undertow.Undertow;
import io.undertow.UndertowOptions;
import io.undertow.server.RoutingHandler;
import io.undertow.util.Headers;
import org.apache.log4j.Logger;
import org.ini4j.Ini;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class Httpd implements Runnable {
    private Logger logger = ClientApplication.getLogger(Httpd.class);
    public static Map<Integer, BytesBuilder> dataCache = HashObjObjMaps.newMutableMap();
    private int port;
    private String ip;

    public Httpd(String ip, int port) {
        this.port = port;
        this.ip = ip;
    }

    private synchronized void putData(Integer key, byte[] data){
        BytesBuilder bb = new BytesBuilder();
        dataCache.put(key, bb);
        bb.append(data);
    }

    public static synchronized byte[] getData(Integer key){
        if(dataCache.containsKey(key)){
            return dataCache.get(key).toArray();
        }else{
            return new byte[0];
        }
    }

    @Override
    public void run() {
        final RoutingHandler handler = new RoutingHandler();
        ClientApplication.initIniConfig();
        if (ClientApplication.ini != null && ClientApplication.ini.containsKey("httpd")) {
            Ini.Section section = ClientApplication.ini.get("httpd");
            Set<Map.Entry<String, String>> entries = section.entrySet();
            Map<Integer, JSONObject> services = new HashMap<>();
            for(Map.Entry<String, String> entity : entries){
                String key = entity.getKey();
                String[] split = key.split("\\.");
                Integer clientId = Integer.valueOf(split[1]);
                if(split.length == 2){
                    JSONObject jo;
                    if(services.containsKey(clientId)){
                        jo = services.get(clientId);
                    }else{
                        jo = new JSONObject();
                    }
                    jo.put(split[0], entity.getValue());
                    services.put(clientId, jo);
                }
            }
            Set<Map.Entry<Integer, JSONObject>> entries1 = services.entrySet();
            for(Map.Entry<Integer, JSONObject> entity : entries1){
                JSONObject value = entity.getValue();
                handler.post(value.getString("url"), exchange -> {
                    exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, value.getString("contentType"));
                    exchange.getRequestReceiver().receiveFullBytes(
                            (exchange1, message) -> {
                                putData(entity.getKey(), message);
                                PushExecuterImpl.triger(entity.getKey(), message);
                                exchange.getResponseSender().send(value.getString("success"));
                            },
                            (exchange12, exception) -> {
                                logger.debug(exception.getMessage());
                                exchange.getResponseSender().send(value.getString("fail"));
                            }
                    );
                });
                logger.debug("Registry url: " + entity.getValue());
            }
        }

        Undertow server = Undertow.builder()
                .addHttpListener(this.port, this.ip)
                .setHandler(handler)
                .setIoThreads(ClientApplication.ioThreads)
                .setWorkerThreads(ClientApplication.workerThreads)
                .setBufferSize(1024)
                .setDirectBuffers(true)
                .setServerOption(UndertowOptions.MAX_ENTITY_SIZE, (long)1024 * 1024 * 1024)
                .build();
        server.start();
        ClientApplication.syslogInfo(String.format("Httpd push server bind port %s success", port));

        Object lock = new Object();
        synchronized (lock) {
            try {
                lock.wait();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                ClientApplication.syslogInfo("Httpd server interrupted while waiting for stop signal");
            }
        }

        server.stop();
    }
}
